package com.xci.platform.auth.service;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.github.pagehelper.Page;
import com.xci.platform.annotation.OperateUser;
import com.xci.platform.annotation.QueryMap;
import com.xci.platform.auth.core.*;
import com.xci.platform.auth.dao.ObjectDataMapDao;
import com.xci.platform.auth.dao.ObjectMapDao;
import com.xci.platform.auth.dao.UserDao;
import com.xci.platform.auth.dao.UserRoleMapDao;
import com.xci.platform.auth.entity.ObjectDataMapEntity;
import com.xci.platform.auth.entity.RoleEntity;
import com.xci.platform.auth.entity.UserEntity;
import com.xci.platform.core.BoolMessage;
import com.xci.platform.core.GMap;
import com.xci.platform.core.Simple;
import com.xci.platform.helper.CoreHelper;
import com.xci.platform.helper.JsonHelper;
import com.xci.platform.helper.StringHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 系统用户服务
 */
@Service
public class UserService extends AuthBaseService {

    /**
     * 用户数据访问对象
     */
    @javax.annotation.Resource
    private UserDao userDao;

    /**
     * 系统用户角色映射数据访问对象
     */
    @javax.annotation.Resource
    private UserRoleMapDao userRoleMapDao;

    /**
     * 对象资源关联数据访问接口
     */
    @javax.annotation.Resource
    private ObjectMapDao objectMapDao;

    /**
     * 对象资源数据访问关联数据访问接口
     */
    @javax.annotation.Resource
    private ObjectDataMapDao objectDataMapDao;

    /**
     * 用户登录
     *
     * @param account  账号
     * @param password 密码
     * @return 登录成功返回true
     */
    public BoolMessage<UserEntity> login(String account, String password) {
        //region 检测密码强度

        BoolMessage result = validPasswordStrong(password);
        if (!result.isSuccess()) {
            return new BoolMessage<>(false, result.getMsg());
        }

        //endregion

        account = account.trim();
        UserEntity entity = userDao.queryByAccount(account);

        //region 检测条件

        if (entity == null) {
            return new BoolMessage<>(false, "账号密码错误");
        }

        String encryptedPwd = encryptPassword(account, password, entity.getSecretKey());
        if (!entity.getPassword().equals(encryptedPwd)) {
            return new BoolMessage<>(false, "账号密码错误");
        }

        if (entity.getStatus() == 0) {
            return new BoolMessage<>(false, "账号已被禁用");
        }

        //endregion

        return new BoolMessage<>(true, entity);
    }

    /**
     * 验证用户密码强度
     *
     * @param password 密码
     * @return 密码符合强度要求返回true
     */
    public BoolMessage validPasswordStrong(String password) {
        int min = 6, max = 30;
        String msg = StringHelper.format("不满足密码策略，密码要求必须包含数字、小写或大写字母、特殊字符、不少于6个字符。");
        if (StringHelper.isBlank(password) || password.length() < min || password.length() > max) {
            return new BoolMessage(false, msg);
        }
        if (AuthHelper.PasswordStrongPattern.matcher(password).matches()) {
            return BoolMessage.True;
        }
        return new BoolMessage(false, msg);
    }

    /**
     * 加密用户密码
     *
     * @param account   账号
     * @param password  密码
     * @param secretKey 秘钥
     * @return 返回加密后的密文
     */
    public String encryptPassword(String account, String password, String secretKey) {
        String accountMd5 = SecureUtil.md5(account);
        String passwordMd5 = SecureUtil.md5(password);
        String secretKeyMd5 = SecureUtil.md5(secretKey);
        return SecureUtil.md5(secretKeyMd5 + passwordMd5 + accountMd5 + secretKeyMd5);
    }

    /// <summary>
    /// 生成用户密码秘钥
    /// </summary>
    public String createSecretkey() {
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DESede.getValue()).getEncoded();
        SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DESede, key);
        String sj1 = SecureUtil.md5(StringHelper.guid());
        String sj2 = SecureUtil.md5(StringHelper.guid());
        return SecureUtil.md5(sj1 + sj2);
    }

    /**
     * 保存用户
     *
     * @param entity   用户对象
     * @param isCreate 是否新建
     */
    @Validated
    @OperateUser
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage save(UserEntity entity, Boolean isCreate) {
        //region 检测密码
        if (isCreate && StringHelper.isBlank(entity.getPassword())) {
            return new BoolMessage(false, "请指定用户密码");
        }
        //endregion

        //region 检测密码强度
        if (isCreate) {
            BoolMessage strongResult = validPasswordStrong(entity.getPassword());
            if (!strongResult.isSuccess()) {
                return strongResult;
            }
        }
        //endregion

        //添加名称简拼
        if (StringHelper.isBlank(entity.getSpell())) {
            entity.setSpell(StringHelper.getSpell(entity.getName()));
        }

        //账号验证
        if (userDao.existByAccount(entity.getAccount(), entity.getId()) > 0) {
            String msg = StringHelper.format("用户账号 {} 已经存在", entity.getAccount());
            return new BoolMessage(false, msg);
        }

        String msg;
        long startTime = CoreHelper.startWatch();
        if (isCreate) {//新建用户
            entity.setLoginCount(0L);
            entity.setIsAdmin(0);
            entity.setSecretKey(createSecretkey());
            entity.setPassword(encryptPassword(entity.getAccount(), entity.getPassword(), entity.getSecretKey()));
            msg = StringHelper.format("新增用户 {}({})", entity.getAccount(), entity.getName());
            userDao.insert(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Insert)
                    .keyValue(entity.getId())
                    .message(msg)
                    .after(entity)
            );
            //endregion
        } else {
            msg = StringHelper.format("修改用户 {}({})", entity.getAccount(), entity.getName());
            UserEntity oldEntity = queryById(entity.getId());
            userDao.update(entity);
            oldEntity.setPassword(null);//清空防止泄密
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Update)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(oldEntity)
                    .after(entity)
            );
            //endregion
        }
        // 操作日志
        operateLog(AuthConstant.SystemModule.User, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 删除用户
     *
     * @param ids      用户主键数组
     * @param authUser 当前登录
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage delete(@NotEmpty(message = "请指定删除的用户主键数组") String[] ids, AuthUser authUser) {
        BoolMessage result = BoolMessage.True;
        for (String id : ids) {
            UserEntity oldEntity = userDao.queryById(id);
            if (oldEntity == null) continue;
            //检测当前账号是否是自身,不允许自己删除自己
            if (oldEntity.getId().equals(authUser.getUserId())) {
                result = BoolMessage.merge(result, BoolMessage.fail("不允许删除自己"));
                continue;
            }
            long startTime = CoreHelper.startWatch();
            userDao.delete(id);
            String msg = StringHelper.format("删除用户 {}", oldEntity.getName());
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Delete)
                    .keyValue(id)
                    .message(msg)
                    .before(oldEntity)
            );
            //endregion
            // 操作日志
            operateLog(AuthConstant.SystemModule.User, msg, CoreHelper.stopWatch(startTime));
        }
        return result;
    }

    /**
     * 更新用户启用状态
     *
     * @param id     用户主键
     * @param status 是否启用(1:启用 0:禁用)
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage updateStatus(@NotNull(message = "请指定用户主键") String id,
                                    @NotNull(message = "请指定状态") Integer status) {
        if (userDao.existById(id) == 0) {
            return new BoolMessage(false, "无效的用户主键");
        }
        long startTime = CoreHelper.startWatch();
        userDao.updateStatus(id, status);
        String msg = StringHelper.format("更新用户状态 主键: {} 更新后状态: {}", id, status);// 操作日志
        operateLog(AuthConstant.SystemModule.User, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 更新用户登录状态
     *
     * @param userEntity 用户对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateLoginStatus(UserEntity userEntity) {
        LoginStatusModel loginStatusModel = new LoginStatusModel();
        loginStatusModel.setId(userEntity.getId());
        loginStatusModel.setLastVisit(new Date());
        if (userEntity.getFirstVisit() == null) {
            loginStatusModel.setFirstVisit(new Date());
        }
        userDao.updateLoginStatus(loginStatusModel);
    }

    /**
     * 根据部门主键更新部门名称
     *
     * @param departmentId   部门主键
     * @param departmentName 部门名称
     */
    public BoolMessage updateDeptNameByDeptId(String departmentId, String departmentName) {
        userDao.updateDeptNameByDeptId(departmentId, departmentName);
        return BoolMessage.True;
    }

    /**
     * 修改用户密码
     *
     * @param id              用户主键
     * @param currentPassword 当前密码
     * @param newPassword     新密码
     */
    public BoolMessage modifyPassword(String id, String currentPassword, String newPassword) {
        //验证用户主键
        UserEntity entity = userDao.queryById(id);
        if (entity == null) {
            return new BoolMessage(false, "无效的用户主键");
        }

        //验证密码强度
        BoolMessage result = validPasswordStrong(newPassword);
        if (!result.isSuccess()) {
            return new BoolMessage(false, result.getMsg());
        }

        //验证当前密码
        if (!entity.getPassword().equals(encryptPassword(entity.getAccount(), currentPassword, entity.getSecretKey()))) {
            return new BoolMessage(false, "当前密码输入不正确,请重新输入");
        }

        //生成新的密钥和密码
        String account = entity.getAccount();
        String secretkey = createSecretkey();
        String uPassword = encryptPassword(account, newPassword, secretkey);

        //更新并添加日志
        long startTime = CoreHelper.startWatch();
        userDao.modifyPassword(id, secretkey, uPassword);
        String msg = StringHelper.format("修改用户密码 主键: {} 账号: {} 姓名: {}", entity.getId(), entity.getAccount(), entity.getName());
        operateLog(AuthConstant.SystemModule.User, msg, CoreHelper.stopWatch(startTime));// 操作日志
        return BoolMessage.True;
    }

    /**
     * 重置用户登录密码
     *
     * @param ids      用户主键数组
     * @param password 新密码
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage revisePassword(String[] ids, String password) {
        //验证密码强度
        BoolMessage result = validPasswordStrong(password);
        if (!result.isSuccess()) {
            return new BoolMessage(false, result.getMsg());
        }

        for (String id : ids) {
            String account = userDao.queryAccountById(id);
            if (StringHelper.isBlank(account)) continue;

            String secretkey = createSecretkey();
            String newPassword = encryptPassword(account, password, secretkey);

            long startTime = CoreHelper.startWatch();
            userDao.modifyPassword(id, secretkey, newPassword);
            String msg = StringHelper.format("修改用户密码 账号: {} 主键: {}", id, account);
            operateLog(AuthConstant.SystemModule.User, msg, CoreHelper.stopWatch(startTime));// 操作日志
        }
        return BoolMessage.True;
    }

    /**
     * 根据用户主键获取用户部门主键
     *
     * @param id 用户主键
     * @return 用户部门主键
     */
    @Validated
    public String queryDepartmentIdById(@NotNull(message = "请指定用户主键") String id) {
        return userDao.queryDepartmentIdById(id);
    }

    /**
     * 根据用户主键获取用户对象
     *
     * @param id 用户主键
     * @return 返回指定用户主键的用户对象
     */
    @Validated
    public UserEntity queryById(@NotNull(message = "请指定用户主键") String id) {
        return userDao.queryById(id);
    }

    /**
     * 根据用户账号获取用户对象
     *
     * @param account 用户账号
     * @return 用户对象
     */
    public UserEntity queryByAccount(String account) {
        return userDao.queryByAccount(account);
    }

    /**
     * 根据角色主键查询用户列表
     *
     * @param roleId 角色主键
     */
    public List<UserEntity> queryByRoleId(@NotNull(message = "请指定角色主键") String roleId) {
        return userRoleMapDao.queryUser(roleId, GMap.newMap());
    }

    /**
     * 根据部门主键查询用户列表
     *
     * @param departmentId 部门主键
     * @param name         用户过滤关键字
     */
    @QueryMap
    public List<UserEntity> queryByDepartmentId(@NotNull(message = "请指定部门主键") String departmentId, String name) {
        GMap map = new GMap();
        map.put("departmentId", departmentId);
        map.put("name", name);
        map.put("status", 1);
        return userDao.query(map);
    }

    /**
     * 查询用户列表
     *
     * @param params 查询参数
     * @return 返回符合查询条件的用户列表
     */
    @QueryMap
    public List<UserEntity> query(Map params) {
        return userDao.query(params);
    }

    /**
     * 获取用户简单对象列表
     *
     * @param params 查询参数
     */
    public List<Simple> querySimple(Map params) {
        return userDao.querySimple(params);
    }

    /**
     * 生成历史记录对象
     */
    private HistoryModel.HistoryModelBuilder buildHistory() {
        return HistoryModel.builder()
                .tableName("sys_user")
                .tableCaption("系统用户")
                .keyName("id");
    }

    //region 用户关联的角色

    /**
     * 添加用户关联的角色
     *
     * @param userId  用户主键
     * @param roleIds 角色主键数组
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage insertMapRole(String userId, String[] roleIds) {
        if (!ObjectUtils.isEmpty(roleIds)) {
            userRoleMapDao.saveRole(userId, roleIds);
        }
        return BoolMessage.True;
    }

    /**
     * 删除用户关联的角色
     *
     * @param userId  用户主键
     * @param roleIds 角色主键数组
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage deleteMapRole(String userId, String[] roleIds) {
        if (!ObjectUtils.isEmpty(roleIds)) {
            userRoleMapDao.deleteRole(userId, roleIds);
        }
        return BoolMessage.True;
    }

    /**
     * 查询用户关联的角色分页列表
     */
    @QueryMap
    public Page<RoleEntity> queryMapRolePageList(String userId, Map params) {
        return (Page<RoleEntity>) userRoleMapDao.queryRole(userId, params);
    }

    /**
     * 查询用户未关联的角色分页列表
     */
    @QueryMap
    public Page<RoleEntity> queryUnMapRolePageList(String userId, Map params) {
        return (Page<RoleEntity>) userRoleMapDao.queryUnRole(userId, params);
    }

    //endregion

    //region 用户关联模块

    /**
     * 保存用户关联的模块
     *
     * @param userId    用户主键
     * @param moduleIds 模块主键数组
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage saveMapModule(String userId, String[] moduleIds) {
        objectMapDao.delete(AuthConstant.Resource.User, userId);
        if (!ObjectUtils.isEmpty(moduleIds)) {
            objectMapDao.insert(AuthConstant.Resource.User, userId,
                    AuthConstant.Resource.Module, moduleIds);
        }
        return BoolMessage.True;
    }

    /**
     * 获取用户关联的模块主键列表
     *
     * @param userId 用户主键
     */
    public List<String> queryMapModuleIdList(String userId) {
        return objectMapDao.queryTargetIds(AuthConstant.Resource.User,
                userId, AuthConstant.Resource.Module);
    }

    //endregion

    //region 用户数据设置关联

    /**
     * 保存用户数据配置关联信息
     *
     * @param userId      用户主键
     * @param dataSetting 数据配置对象
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage saveMapDataSetting(String userId, AuthDataSetting dataSetting) {
        ObjectDataMapEntity entity = new ObjectDataMapEntity();
        entity.setObjectId(userId);
        entity.setObjectName(AuthConstant.Resource.User);
        entity.setDataSetting(JsonHelper.serialize(dataSetting));
        objectDataMapDao.delete(entity.getObjectName(), entity.getObjectId());
        objectDataMapDao.insert(entity);
        return BoolMessage.True;
    }

    /**
     * 查询用户数据配置关联信息
     *
     * @param userId 用户主键
     */
    public AuthDataSetting queryMapDataSetting(String userId) {
        AuthDataSetting dataSetting = new AuthDataSetting();
        String dataString = objectDataMapDao.queryDataSetting(AuthConstant.Resource.User, userId);
        if (StringHelper.isNotBlank(dataString)) {
            dataSetting = JsonHelper.deserialize(dataString, AuthDataSetting.class);
        }
        if (dataSetting.isNone()){
            dataSetting = AuthDataSetting.Self;
        }
        return dataSetting;
    }

    /**
     * 查询用户拥有的数据配置信息
     *
     * @param userId 用户主键
     */
    public AuthDataSetting queryOwnDataSetting(String userId) {
        List<String> datas = objectDataMapDao.queryDataSettingByUserId(userId);
        return AuthDataSetting.None.merge(datas);
    }

    //endregion
}
